package repo

import (
	"fmt"
	log "github.com/sirupsen/logrus"
	model "micro-oauth/datamodels"
	"micro-oauth/datamodels/params"
)

func (s *ServiceImpl) ReplaceUserRoles(user *model.User, roles []*model.Role) error {
	err := s.db.Model(&user).Association("Roles").Replace(roles).Error
	if err != nil {
		return err
	}
	var roleIds = make([]uint64, 0)
	for _, role := range roles {
		roleIds = append(roleIds, role.Id)
	}
	err = s.cacheService.DelUserRoleIds(user.Id)
	LogError(err, "del user role ids")
	err = s.cacheService.SetUserRoleIds(user.Id, roleIds)
	LogError(err, "set user role ids")
	return nil
}

func (s *ServiceImpl) AddRole(role *model.Role) error {
	//  是否会有关联数据也被创建? 答:不会被关联创建
	return s.db.Create(role).Error
}

func (s *ServiceImpl) UpdateRole(role *model.Role) error {
	return s.db.Save(role).Error
}

func (s *ServiceImpl) ListRoles(req *params.RoleListRequest) (roles []*model.Role, total uint64, err error) {
	db := DefaultListDB(s.db, &req.ListRequest)
	db = db.Where("scope_id = ?", req.ScopeId)
	if req.Name != "" {
		db = db.Where("name = ?", req.Name)
	}
	if req.Code != "" {
		db = db.Where("code = ?", req.Code)
	}
	if len(req.UserIds) != 0 {
		db = db.Joins("join oauth_user_role_relation as ur on ur.role_id = oauth_role.id and ur.user_id in (?)", req.UserIds)
	}
	if req.IncludePermission {
		db = db.Preload("PermissionCodes")
	}
	db.Model(model.Role{}).Count(&total)
	db = LimitDB(db, req.Limit, req.Offset)
	err = db.Find(&roles).Error
	if err != nil || len(roles) == 0 {
		return make([]*model.Role, 0), 0, err
	}
	return roles, total, nil
}

func (s *ServiceImpl) GetRoleById(roleId uint64) *model.Role {
	var role model.Role
	err := s.db.Where("id = ? ", roleId).First(&role).Error
	if err != nil {
		return nil
	}
	return &role
}

func (s *ServiceImpl) ListRolePermissionInfos(roleId uint64) ([]*model.RolePermissionInfo, error) {
	permissionInfos, err := s.cacheService.GetRolePermissionInfos(roleId)
	if err != nil {
		LogError(err, "repoCache.GetRolePermissionInfos")
	} else {
		return permissionInfos, nil
	}
	permissionInfos = make([]*model.RolePermissionInfo, 0)
	sql := `select RPI.role_id, RPI.permission_id, RPI.need_auth, oauth_permission.code 
        from oauth_role_permission_info AS RPI
		inner join oauth_permission on oauth_permission.id = RPI.permission_id
		where role_id = ?`
	rows, err := s.db.Raw(sql, roleId).Rows()
	if err != nil {
		LogError(err, "select role permission infos")
		return nil, err
	}
	defer func() {
		if err := rows.Close(); err != nil {
			log.Error("close rows error: " + err.Error())
		}
	}()
	for rows.Next() {
		var roleId uint64
		var permissionId uint64
		var needOauth bool
		var permissionCode string
		err := rows.Scan(&roleId, &permissionId, &needOauth, &permissionCode)
		if err != nil {
			return nil, err
		}
		permissionInfos = append(permissionInfos, &model.RolePermissionInfo{
			RoleId:         roleId,
			PermissionId:   permissionId,
			NeedAuth:       needOauth,
			PermissionCode: permissionCode,
		})
	}
	if len(permissionInfos) > 0 {
		err = s.cacheService.SetRolePermissionInfos(roleId, permissionInfos)
		LogError(err, "cacheService.SetRolePermissionInfos")
	}
	return permissionInfos, nil
}

func (s *ServiceImpl) DeleteRole(roleId uint64) error {
	return s.db.Unscoped().Where("id = ?", roleId).Delete(&model.Role{}).Error
}

func (s *ServiceImpl) ReplaceRolePermissionInfos(role *model.Role, permissionInfos []*model.RolePermissionInfo) error {
	tx := s.db.Begin()
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()
	err := tx.Delete(&model.RolePermissionInfo{}, "role_id = ?", role.Id).Error
	if err != nil {
		tx.Rollback()
		return err
	}

	if len(permissionInfos) == 0 {
		tx.Commit()
		err = s.cacheService.DelRolePermissionInfos(role.Id)
		LogError(err, "cacheService.DelRolePermissionInfos")
		return nil
	}
	sqlTemplate := "insert into oauth_role_permission_info (role_id, permission_id, need_auth) values(%d, %d, %t);\n"
	sql := ""
	for _, p := range permissionInfos {
		sql += fmt.Sprintf(sqlTemplate, role.Id, p.PermissionId, p.NeedAuth)
	}
	if sql != "" {
		err = tx.Exec(sql).Error
		if err != nil {
			tx.Rollback()
			return err
		}
	}
	tx.Commit()
	err = s.cacheService.DelRolePermissionInfos(role.Id)
	LogError(err, "cacheService.DelRolePermissionInfos")
	return nil
}

func (s *ServiceImpl) clearRolePermissions(roleId uint64) error {
	return s.db.Delete(&model.RolePermissionInfo{}, "role_id = ?", roleId).Error
}
